AdderFusion

用 L1 距离(绝对差之和)替代标准卷积中的点积。输入、权重、输出均为 NHWC。 假定输入为 \(X\)、卷积核为 \(F\),按以下公式计算:

\[Y(m,n,t) = - \sum_{i=0}^{d} \sum_{j=0}^{d} \sum_{k=0}^{C_{in}} |X(m+i, n+j, k) - F(i,j,k,t)|\]
输入:
  • input_x - 输入数据地址

  • input_w - 卷积核权重地址

  • bias - 偏置地址

  • conv_param - ConvParameter 结构体地址,字段见下

  • core_mask - 核掩码(仅共享存储版本)

ConvParameter 定义:

 1typedef struct ConvParameter {
 2    void* workspace_;      // 仅 *_p 使用;*_s 不读(见 警告)
 3    int output_batch_;     // 输出 batch
 4    int input_batch_;      // 输入 batch
 5    int input_h_;          // 输入高
 6    int input_w_;          // 输入宽
 7    int output_h_;         // 输出高
 8    int output_w_;         // 输出宽
 9    int input_channel_;    // 输入通道
10    int output_channel_;   // 输出通道
11    int kernel_h_;         // 卷积核高
12    int kernel_w_;         // 卷积核宽
13    int group_;            // 组数
14    int pad_l_;            // 左填充
15    int pad_u_;            // 上填充
16    int dilation_h_;       // 高方向膨胀
17    int dilation_w_;       // 宽方向膨胀
18    int stride_h_;         // 高方向步长
19    int stride_w_;         // 宽方向步长
20    int buffer_size_;      // 仅 *_p:元素个数;*_s 不读
21} ConvParameter;
输出:
  • out_y - 输出地址

支持平台:

FT78NE MT7004

警告

MT7004的workspace_ / buffer_size_ 分配算法(与前向 conv2d 相同;与 conv2d_transpose 不同:本算子 _s 不用 DDR workspace):

  1. 共享版 _s 不读 workspace_ / buffer_size_。LA 写死每核 AM 0x10000000,内部 BUFFER_SIZE = 65536 元素;布局 [im2col | packed weights] 各占一半。Host 无需在 DDR 上按 buffer_size_ 申请 workspace(可填 NULL / 0)。

  2. C 参考与生产 LA 不一致:C golden 用 DDR workspace + logic_core_id × 2048;LA _s 用每核私有 AM。

  3. 私有版 _p 才读这两个字段。令 k = Kh × Kw × (Cin / G), chunk = buffer_size_ / k,须 buffer_size_ ≥ k(常用 max(2048, k))。 workspace 字节:

    • fp 私有 _p:buffer_size_ × sizeof(float)(仅 im2col)

    • hp 私有 _p:2 × buffer_size_ × sizeof(half)(im2col + 权重 pack)

  4. 推荐(_p):

    k = Kh * Kw * (Cin / G);
    buffer_size_ = max(2048, k);   /* 元素个数 */
    /* fp: */
    workspace_bytes = buffer_size_ * sizeof(float);
    /* hp: */
    workspace_bytes = 2 * buffer_size_ * sizeof(half);
    

备注

  • FT78NE 支持 int8、fp32

  • MT7004 支持 fp16、fp32

共享存储版本:

void i8_adder_s(int8_t *input_x, int8_t *input_w, int8_t *out_y, int *bias, ConvParameter *conv_param, int core_mask)
void hp_adder_s(float16 *input_x, float16 *input_w, float16 *out_y, float16 *bias, ConvParameter *conv_param, int core_mask)
void fp_adder_s(float *input_x, float *input_w, float *out_y, float *bias, ConvParameter *conv_param, int core_mask)

C调用示例:

 1// MT7004 示例(共享存储多核,DDR 地址)
 2void TestAdderSMCFp32(int *input_shape, int *weight_shape, int *output_shape,
 3                      int *stride, int *padding, int *dilation, int groups,
 4                      float *bias, int core_mask) {
 5    int core_id = get_core_id();
 6    int logic_core_id = GetLogicCoreId(core_mask, core_id);
 7    int core_num = GetCoreNum(core_mask);
 8    float *input_data = (float *)0x88000000;
 9    float *weight = (float *)0x89000000;
10    float *output_data = (float *)0x90000000;
11    float *bias_data = (float *)0x91000000;
12    ConvParameter *param = (ConvParameter *)0x92000000;
13    if (logic_core_id == 0) {
14        memcpy(bias_data, bias, sizeof(float) * output_shape[3]);
15        param->dilation_h_ = dilation[0];
16        param->dilation_w_ = dilation[1];
17        param->group_ = groups;
18        param->input_batch_ = input_shape[0];
19        param->input_h_ = input_shape[1];
20        param->input_w_ = input_shape[2];
21        param->input_channel_ = input_shape[3];
22        param->kernel_h_ = weight_shape[1];
23        param->kernel_w_ = weight_shape[2];
24        param->output_batch_ = output_shape[0];
25        param->output_h_ = output_shape[1];
26        param->output_w_ = output_shape[2];
27        param->output_channel_ = output_shape[3];
28        param->stride_h_ = stride[0];
29        param->stride_w_ = stride[1];
30        param->pad_u_ = padding[0];
31        param->pad_l_ = padding[2];
32        /* *_s 不读这两个字段;LA 使用每核 AM 0x10000000,BUFFER_SIZE=65536 */
33        param->workspace_ = NULL;
34        param->buffer_size_ = 0;
35    }
36    sys_bar(0, core_num);
37    fp_adder_s(input_data, weight, output_data, bias_data, param, core_mask);
38}
39
40void main() {
41    int in_channel = 4;
42    int out_channel = 4;
43    int groups = 2;
44    int input_shape[4] = {1, 18, 18, in_channel};   // NHWC
45    int weight_shape[4] = {out_channel, 3, 3, in_channel / groups};
46    int output_shape[4] = {1, 16, 16, out_channel};  // NHWC
47    int stride[2] = {1, 1};
48    int padding[4] = {0, 0, 0, 0};
49    int dilation[2] = {1, 1};
50    float bias[] = {1.0f, 2.0f, 3.0f, 4.0f};
51    int core_mask = 0b1111;
52    TestAdderSMCFp32(input_shape, weight_shape, output_shape,
53                     stride, padding, dilation, groups, bias, core_mask);
54}

私有存储版本:

void i8_adder_p(int8_t *input_x, int8_t *input_w, int8_t *out_y, int *bias, ConvParameter *conv_param)
void hp_adder_p(float16 *input_x, float16 *input_w, float16 *out_y, float16 *bias, ConvParameter *conv_param)
void fp_adder_p(float *input_x, float *input_w, float *out_y, float *bias, ConvParameter *conv_param)

C调用示例:

 1// MT7004 示例(私有存储单核,AM 地址)
 2void TestAdderL2Fp32(int *input_shape, int *weight_shape, int *output_shape,
 3                     int *stride, int *padding, int *dilation, int groups,
 4                     float *bias) {
 5    float *input_data = (float *)0x10010000;
 6    float *weight = (float *)0x10020000;
 7    float *output_data = (float *)0x10030000;
 8    float *bias_data = (float *)0x10040000;
 9    ConvParameter *param = (ConvParameter *)0x10060000;
10    memcpy(bias_data, bias, sizeof(float) * output_shape[3]);
11    param->dilation_h_ = dilation[0];
12    param->dilation_w_ = dilation[1];
13    param->group_ = groups;
14    param->input_batch_ = input_shape[0];
15    param->input_h_ = input_shape[1];
16    param->input_w_ = input_shape[2];
17    param->input_channel_ = input_shape[3];
18    param->kernel_h_ = weight_shape[1];
19    param->kernel_w_ = weight_shape[2];
20    param->output_batch_ = output_shape[0];
21    param->output_h_ = output_shape[1];
22    param->output_w_ = output_shape[2];
23    param->output_channel_ = output_shape[3];
24    param->stride_h_ = stride[0];
25    param->stride_w_ = stride[1];
26    param->pad_u_ = padding[0];
27    param->pad_l_ = padding[2];
28    /* *_p:buffer_size_ 元素个数,须 >= Kh*Kw*(Cin/G);FP workspace = 1*buffer_size_ */
29    param->workspace_ = (float *)0x10070000;
30    param->buffer_size_ = 2048;
31    fp_adder_p(input_data, weight, output_data, bias_data, param);
32}
33
34void main() {
35    int in_channel = 4;
36    int out_channel = 4;
37    int groups = 2;
38    int input_shape[4] = {1, 18, 18, in_channel};   // NHWC
39    int weight_shape[4] = {out_channel, 3, 3, in_channel / groups};
40    int output_shape[4] = {1, 16, 16, out_channel};  // NHWC
41    int stride[2] = {1, 1};
42    int padding[4] = {0, 0, 0, 0};
43    int dilation[2] = {1, 1};
44    float bias[] = {1.0f, 2.0f, 3.0f, 4.0f};
45    TestAdderL2Fp32(input_shape, weight_shape, output_shape,
46                    stride, padding, dilation, groups, bias);
47}